/**
 * 
 */
package eu.reply.android.flashm.common;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.text.ParseException;

import org.apache.http.MalformedChunkCodingException;
import org.apache.http.auth.InvalidCredentialsException;
import org.apache.http.impl.io.ChunkedInputStream;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;

import eu.reply.android.flashm.exceptions.BEServiceNetException;

import android.util.Base64;
import android.util.Log;

/**
 * Synchronous HTTP connection.
 * 
 * @see HttpAsyncConnection
 */
public class HttpConnection {

    private static final String TAG = "HttpConnection";
    HttpRequest httpRequest;

    /**
     * Create an instance of a synchronous HTTP connection
     * 
     * @param request
     *            The HTTP request indication URL, content and HTTP method
     * */
    public HttpConnection(HttpRequest request) {
	this.httpRequest = request;
    }

    /**
     * The method executes the specified http request and blocks until a
     * response is received.
     * 
     * @return The HttpResponse that encapsulates the response from the http
     *         connection
     * @throws BEServiceNetException
     *             Connectivity problem. For example, if the host cannot be
     *             reached (no internet access or blocking firewall or wrong URL
     *             specified)
     */
    public HttpResponse connect() throws BEServiceNetException {
	HttpURLConnection conn = null;
	HttpResponse resp = null;
	try {
	    /*
	     * do to a bug in android, set the sytem property keepAlive to false
	     * in order to avoid getting empty inputStreams
	     * 
	     * http://stackoverflow.com/questions/1440957/httpurlconnection-
	     * getresponsecode-returns-1-on-second-invocation
	     * http://code.google.com/p/android/issues/detail?id=7786
	     */
	    System.setProperty("http.keepAlive", "false");

	    conn = (HttpURLConnection) this.httpRequest.getUrl()
		    .openConnection();

	    conn.setDoInput(true);
	    conn.setAllowUserInteraction(false);
	    conn.setUseCaches(false);
	    // TODO: check if 10 seconds is a tolerable value
	    conn.setConnectTimeout(10000);
	    conn.setReadTimeout(5000);

	    String method = this.httpRequest.getMessageType().toString();

	    conn.setRequestMethod(method);

	    // if there is authentication involved, add the http header
	    String user = this.httpRequest.getUser();
	    if (user != null) {
		// prepare the Authentication header
		String httpBasicAuthValue = user + ":"
			+ this.httpRequest.getPwd();
		httpBasicAuthValue = Base64.encodeToString(
			httpBasicAuthValue.getBytes(), Base64.DEFAULT
				| Base64.NO_WRAP);
		conn.setRequestProperty("Authorization", "Basic "
			+ httpBasicAuthValue);
	    }
	    //initDefaultAuthenticator();

	    // if there is content in the request, add the http header
	    byte[] data = this.httpRequest.getContent();
	    if (data != null) {
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type",
			this.httpRequest.getContentType());
		conn.setRequestProperty("Content-Length",
			Integer.toString(data.length));

		// output the content
		OutputStream out = conn.getOutputStream();
		out.write(data);
		out.close();
	    }

	    int responseCode = conn.getResponseCode();
	    int contentLength = conn.getContentLength();
	    String contentType = conn.getContentType();
	    String contentEncoding = conn.getContentEncoding();
	    String transferEncoding = conn.getHeaderField("Transfer-Encoding");
	    String contentTransferEncoding = conn
		    .getHeaderField("Content-Transfer-Encoding");
	    String location = conn.getHeaderField("Location");
	    
	      Log.i(TAG,"Status code: " +responseCode);
	      Log.i(TAG,"Content-Length: " +contentLength);
	      Log.i(TAG,"Content-Type: " +contentType);
	      Log.i(TAG,"Content-Encoding: " +contentEncoding);
	      Log.i(TAG,"Transfer-Encoding: " +transferEncoding);
	      Log.i(TAG,"Content-Transfer-Encoding: "
	      +contentTransferEncoding);
	     
	    String charSet = "UTF-8";
	    if (contentType != null) {
		int indxCharset = contentType.indexOf("charset=");
		if (indxCharset >= 0) {
		    charSet = contentType.substring(indxCharset
			    + "charset=".length());
		    // Log.i(TAG, "detected charset: " + charSet);
		}
	    }

	    InputStream errorIn = conn.getErrorStream();
	    if (/* responseCode >= 400 && */errorIn != null) {
		/* show the error information returned by the server */
		String responseLine = null;
		StringBuilder bld = new StringBuilder();
		BufferedReader reader = new BufferedReader(
			new InputStreamReader(errorIn, charSet), 1024);
		while ((responseLine = reader.readLine()) != null) {
		    bld.append(responseLine);
		}
		String errorContent = bld.toString();
		Log.e(TAG, "Exception because HTTP " + responseCode + "="
			+ errorContent);
		reader.close();

		HttpError httpError = new HttpError(responseCode, errorContent,
			contentType, contentEncoding);

		resp = new HttpResponse(responseCode, null, contentType,
			contentEncoding);

		resp.setError(httpError);

		return resp;

	    }

	    // Get the response content
	    byte[] respContent = null;
	    if (contentLength > 0) {
		respContent = new byte[contentLength];
		int index = 0;
		int bytesRead = 0;
		InputStream stream = conn.getInputStream();

		while ((bytesRead = stream.read(respContent, index,
			contentLength - index)) >= 0) {
		    index += bytesRead;
		}

		stream.close();

	    } else {
		InputStream is = conn.getInputStream();

		StringBuilder bld = new StringBuilder();
		String responseLine = null;
		BufferedReader reader = new BufferedReader(
			new InputStreamReader(is, charSet), 1024);
		while ((responseLine = reader.readLine()) != null) {
		    bld.append(responseLine);
		}
		String strContent = bld.toString();
		respContent = strContent.getBytes();

		reader.close();

	    }

	    // build an httpResponse object
	    resp = new HttpResponse(responseCode, respContent, contentType,
		    contentEncoding);
	    resp.setLocation(location);
	    // Log.i(TAG,"response: "+ new String(respContent,charSet));

	} catch (SocketTimeoutException ex) {
	    Log.i(TAG, "connection time out or reading time out");
	    throw BEServiceNetException.newInstance(ex);
	} catch (IOException ex) {
	    Log.v(TAG, "", ex);
	    throw BEServiceNetException.newInstance(ex);
	} finally {
	    if (conn != null) {
		conn.disconnect();
	    }
	}

	return resp;
    }

    private int mNrOfAuthChallenges;
    /**
     * If an httpChallenge is received, the authenticator will provide the user
     * name and password specified
     * 
     * @param userName
     * @param password
     */
//    private void initDefaultAuthenticator() {
//    	mNrOfAuthChallenges = 0;
//	/*
//	 * Do to an android bug, set the Authentication before making a request.
//	 * Android seems to hang or throw a null pointer exception if the answer
//	 * from the sever contains an authentication challenge
//	 * 
//	 * http://code.google.com/p/android/issues/detail?id=9579
//	 */
//	Authenticator.setDefault(new Authenticator() {
//	    protected PasswordAuthentication getPasswordAuthentication() {
//		String username = null;
//		
//		if(mNrOfAuthChallenges>2){
//			//you might thing returning null is the best option to avoid a endless loop,
//			//of authentications and you are right. But this will crash devices like the HTC magic with android 2.2.1
//			return null;
//		}else{
//			mNrOfAuthChallenges++;
//		}
//
//		Log.i(TAG, "authentication challenge received ");
//		LocalSettings settings = LocalSettings.getInstance();
//		if (settings != null) {
//		    UserProfile profile = settings.getUserProfile();
//		    if (profile != null) {
//			username = profile.getAccountName();
//		    }
//		}
//
//		if (username == null) {
//		    username = "Anonymous";
//		    Log.e(TAG,
//			    "NO CREDENTIALS FOUND FOR HTTP AUTH CHALLENGE!!. Please provide credentials");
//		}
//
//		return new PasswordAuthentication(username, username
//			.toCharArray());
//	    }
//	});
//    }

}
